﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SZHelperCore;

namespace SZHelperCore
{
    public class HttpClientResult
    {
        
        public bool IsSuccess { get; set; }
        public string Message { get; set; }
        public string Data { get; set; }
    }
    public static class HttpClientResultExtensions
    {
        public static Reulst ToData<Reulst>(this HttpClientResult result)
        {
            if(result.IsSuccess)
            {
                if (!string.IsNullOrEmpty(result.Data))
                {
                    Console.WriteLine(result.Data);
                    return JsonConvert.DeserializeObject<Reulst>(result.Data);
                }
                else
                {
                    return default(Reulst);
                }
            }
            else
            {
                throw new Exception(result.Message);
            }
        }
    }
    public class HTTPClientHelper : IDisposable
    {
        private HttpClient _HttpClient;
        private int _TimeOutInMillisecond = 30 * 1000;
        /// <summary>
        /// 可以完全自定义
        /// </summary>
        public HttpClient HttpClient { get => _HttpClient; set => _HttpClient = value; }

        public HTTPClientHelper(int maxCount=3, int timeOutInMillisecond = 30 * 1000)
        {
            HttpMessageHandler handler = new TimeoutHandler(maxCount, timeOutInMillisecond);
            HttpClient = new HttpClient(handler);
            _TimeOutInMillisecond = timeOutInMillisecond;
        }
        public HTTPClientHelper(HttpClientHandler httpClientHandler, int timeOutInMillisecond = 30 * 1000)
        {
            HttpClient = new HttpClient(httpClientHandler);
            _TimeOutInMillisecond = timeOutInMillisecond;
        }
        public HttpClientResult Get(string url)
        {
            return Get(url, null);
        }
        public HttpClientResult Get(string url, Dictionary<string, string> headerArray)
        {
            return Get(url, null, headerArray); 
        }
        public HttpClientResult Get(string url, Dictionary<string, object> paramArray, Dictionary<string, string> headerArray = null)
        {
            HttpClientResult result = new HttpClientResult();
            if (headerArray != null)
            {
                _HttpClient.DefaultRequestHeaders.Clear();

                foreach (var item in headerArray)
                {
                    _HttpClient.DefaultRequestHeaders.Add(item.Key.ToString(), item.Value.ToString());//加头
                }
            }
            var getUrl = url;
            if (paramArray != null)
            {
                getUrl = url + "?" + BuildGetParam(paramArray, Encoding.UTF8);
            }
            Task<HttpResponseMessage> task = _HttpClient.GetAsync(getUrl);
            if (task.Wait(_TimeOutInMillisecond))
            {
                HttpResponseMessage response = task.Result;
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    result.IsSuccess = true;
                    var httpContent = response.Content;
                    Stream myResponseStream = httpContent.ReadAsStreamAsync().Result;
                    StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
                    result.Data = myStreamReader.ReadToEnd();
                    myStreamReader.Close();
                    myResponseStream.Close();
                    return result;
                }
                result.IsSuccess = false;
                result.Message = response.Content.ReadAsStringAsync().Result;
                return result;
            }
            result.IsSuccess = false;
            result.Message = "time out!";
            return result;
        }
        public HttpClientResult PostJson(string url)
        {
            return Post(url, new StringContent(""), null);
        }

        public HttpClientResult PostJson<T>(string url, T obj, Dictionary<string, string> headerArray = null) where T: notnull
        {
            string json = "";
            if (obj != null)
                json = JsonConvert.SerializeObject(obj);
            return PostJson(url, json, headerArray);
        }

        public HttpClientResult PostJson(string url, Dictionary<string, object> paramArray, Dictionary<string, string> headerArray)
        {
            string json = "";
            if (paramArray != null)
                json = JsonConvert.SerializeObject(paramArray);
            return PostJson(url, json, headerArray);
        }
        public HttpClientResult PostJson(string url, string jsonData, Dictionary<string, string> headerArray)
        {
            //HttpContent content = new StringContent(jsonData,Encoding.UTF8, "application/json");
            //content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            //content.Headers.ContentLength = jsonData.Length;

            byte[] byteData = Encoding.UTF8.GetBytes(jsonData);

            using (HttpContent content = new ByteArrayContent(byteData))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue("application/Json");
                content.Headers.ContentLength= byteData.Length;
                return Post(url, content, headerArray);
            }


           
        }
        public HttpClientResult PostForm<T>(string url, T obj, Dictionary<string, string> headerArray)
        {
            string json = "";
            if (obj != null)
                json = JsonConvert.SerializeObject(obj);
            Dictionary<string, object> paramArray = new Dictionary<string, object>();
            foreach (var item in obj.GetType().GetProperties())
            {
                paramArray.Add(item.Name, item.GetValue(obj));
            }
            HttpContent content = new MyFormUrlEncodedContent(paramArray);
            return Post(url, content, headerArray);
        }
        public HttpClientResult PostForm(string url, Dictionary<string, object> paramArray, Dictionary<string, string> headerArray = null)
        {
            HttpContent content = new MyFormUrlEncodedContent(paramArray);
            return Post(url, content, headerArray);
        }
        public HttpClientResult Post(string url, HttpContent content, Dictionary<string, string> headerArray)
        {
            HttpClientResult result = new HttpClientResult();
            if (headerArray != null)
            {
                _HttpClient.DefaultRequestHeaders.Clear();

                foreach (var item in headerArray) 
                {
                    _HttpClient.DefaultRequestHeaders.Add(item.Key.ToString(), item.Value.ToString());//加头
                }
            }
            Task<HttpResponseMessage> task = _HttpClient.PostAsync(url, content);
            if (task.Wait(_TimeOutInMillisecond))
            {
                HttpResponseMessage response = task.Result;
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    result.IsSuccess = true;
                    var httpContent = response.Content;
                    Stream myResponseStream = httpContent.ReadAsStreamAsync().Result;
                    StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8);
                    result.Data = myStreamReader.ReadToEnd();
                    myStreamReader.Close();
                    myResponseStream.Close();
                    return result;
                }
                result.IsSuccess = false;
                result.Message=response.ReasonPhrase;
                return result;
            }
            result.IsSuccess = false;
            result.Message = "time out!";
            return result;
        }

        private string Encode(string content, Encoding encode = null)
        {
            if (encode == null) return content;

            return System.Web.HttpUtility.UrlEncode(content, Encoding.UTF8);
        }

        private string BuildGetParam(Dictionary<string, object> paramArray, Encoding encode = null)
        {
            string url = "";

            if (encode == null) encode = Encoding.UTF8;

            if (paramArray != null && paramArray.Count > 0)
            {
                var parms = "";
                foreach (var item in paramArray)
                {
                    parms += string.Format("{0}={1}&", Encode(item.Key.ToString(), encode), Encode(item.Value.ToString(), encode));
                }
                if (parms != "")
                {
                    parms = parms.TrimEnd('&');
                }
                url += parms;

            }
            return url;
        }
        public class MyFormUrlEncodedContent : ByteArrayContent
        {
            public MyFormUrlEncodedContent(IEnumerable<KeyValuePair<string, object>> nameValueCollection)
                : base(MyFormUrlEncodedContent.GetContentByteArray(nameValueCollection))
            {
                base.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
            }
            private static byte[] GetContentByteArray(IEnumerable<KeyValuePair<string, object>> nameValueCollection)
            {
                StringBuilder stringBuilder = new StringBuilder();
                foreach (KeyValuePair<string, object> current in nameValueCollection)
                {
                    if (stringBuilder.Length > 0)
                    {
                        stringBuilder.Append('&');
                    }

                    stringBuilder.Append(MyFormUrlEncodedContent.Encode(current.Key));
                    stringBuilder.Append('=');
                    stringBuilder.Append(MyFormUrlEncodedContent.Encode(current.Value.ToString()));
                }
                return Encoding.Default.GetBytes(stringBuilder.ToString());
            }

            private static string Encode(string data)
            {
                if (string.IsNullOrEmpty(data))
                {
                    return string.Empty;
                }
                return WebUtility.UrlEncode(data);
            }
        }

        public void Dispose()
        {
            HttpClient.Dispose();
        }

        public class TimeoutHandler : DelegatingHandler
        {
            private int _timeout;
            private int _max_count;
            ///
            /// 超时重试
            ///
            ///重试次数
            ///超时时间
            public TimeoutHandler(int max_count = 3, int timeout = 5000)
            {
                base.InnerHandler = new HttpClientHandler();
                _timeout = timeout;
                _max_count = max_count;
            }

            protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                HttpResponseMessage response = null;
                for (int i = 1; i <= _max_count + 1; i++)
                {
                    var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                    cts.CancelAfter(_timeout);
                    try
                    {
                        response = await base.SendAsync(request, cts.Token);

                        if (response.IsSuccessStatusCode)
                        {
                            return response;
                        }
                    }
                    catch (Exception ex)
                    {
                        //请求超时
                        if (ex is TaskCanceledException)
                        {
                            if (i > _max_count)
                            {
                                return new HttpResponseMessage(HttpStatusCode.OK)
                                {
                                    Content = new StringContent("{\"code\":-1,\"data\":\"\",\"msg\":\"接口请求超时\"}", Encoding.UTF8, "text/json")
                                };
                            }
                        }
                        else
                        {
                            return new HttpResponseMessage(HttpStatusCode.OK)
                            {
                                Content = new StringContent("{\"code\":-1,\"data\":\"\",\"msg\":\"接口请求出错\"}", Encoding.UTF8, "text/json")
                            };
                        }
                    }
                }
                return response;
            }
        }
    }
}